home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / g_ai.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-13  |  25.5 KB  |  1,118 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // g_ai.c
  21.  
  22. #include "g_local.h"
  23.  
  24. qboolean FindTarget (edict_t *self);
  25. extern cvar_t    *maxclients;
  26.  
  27. qboolean ai_checkattack (edict_t *self, float dist);
  28.  
  29. qboolean    enemy_vis;
  30. qboolean    enemy_infront;
  31. int            enemy_range;
  32. float        enemy_yaw;
  33.  
  34. //============================================================================
  35.  
  36.  
  37. /*
  38. =================
  39. AI_SetSightClient
  40.  
  41. Called once each frame to set level.sight_client to the
  42. player to be checked for in findtarget.
  43.  
  44. If all clients are either dead or in notarget, sight_client
  45. will be null.
  46.  
  47. In coop games, sight_client will cycle between the clients.
  48. =================
  49. */
  50. void AI_SetSightClient (void)
  51. {
  52.     edict_t    *ent;
  53.     int        start, check;
  54.  
  55.     if (level.sight_client == NULL)
  56.         start = 1;
  57.     else
  58.         start = level.sight_client - g_edicts;
  59.  
  60.     check = start;
  61.     while (1)
  62.     {
  63.         check++;
  64.         if (check > game.maxclients)
  65.             check = 1;
  66.         ent = &g_edicts[check];
  67.         if (ent->inuse
  68.             && ent->health > 0
  69.             && !(ent->flags & FL_NOTARGET) )
  70.         {
  71.             level.sight_client = ent;
  72.             return;        // got one
  73.         }
  74.         if (check == start)
  75.         {
  76.             level.sight_client = NULL;
  77.             return;        // nobody to see
  78.         }
  79.     }
  80. }
  81.  
  82. //============================================================================
  83.  
  84. /*
  85. =============
  86. ai_move
  87.  
  88. Move the specified distance at current facing.
  89. This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
  90. ==============
  91. */
  92. void ai_move (edict_t *self, float dist)
  93. {
  94.     M_walkmove (self, self->s.angles[YAW], dist);
  95. }
  96.  
  97.  
  98. /*
  99. =============
  100. ai_stand
  101.  
  102. Used for standing around and looking for players
  103. Distance is for slight position adjustments needed by the animations
  104. ==============
  105. */
  106. void ai_stand (edict_t *self, float dist)
  107. {
  108.     vec3_t    v;
  109.  
  110.     if (dist)
  111.         M_walkmove (self, self->s.angles[YAW], dist);
  112.  
  113.     if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  114.     {
  115.         if (self->enemy)
  116.         {
  117.             VectorSubtract (self->enemy->s.origin, self->s.origin, v);
  118.             self->ideal_yaw = vectoyaw(v);
  119.             if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  120.             {
  121.                 self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  122.                 self->monsterinfo.run (self);
  123.             }
  124.             M_ChangeYaw (self);
  125.             ai_checkattack (self, 0);
  126.         }
  127.         else
  128.             FindTarget (self);
  129.         return;
  130.     }
  131.  
  132.     if (FindTarget (self))
  133.         return;
  134.     
  135.     if (level.time > self->monsterinfo.pausetime)
  136.     {
  137.         self->monsterinfo.walk (self);
  138.         return;
  139.     }
  140.  
  141.     if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
  142.     {
  143.         if (self->monsterinfo.idle_time)
  144.         {
  145.             self->monsterinfo.idle (self);
  146.             self->monsterinfo.idle_time = level.time + 15 + random() * 15;
  147.         }
  148.         else
  149.         {
  150.             self->monsterinfo.idle_time = level.time + random() * 15;
  151.         }
  152.     }
  153. }
  154.  
  155.  
  156. /*
  157. =============
  158. ai_walk
  159.  
  160. The monster is walking it's beat
  161. =============
  162. */
  163. void ai_walk (edict_t *self, float dist)
  164. {
  165.     M_MoveToGoal (self, dist);
  166.  
  167.     // check for noticing a player
  168.     if (FindTarget (self))
  169.         return;
  170.  
  171.     if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
  172.     {
  173.         if (self->monsterinfo.idle_time)
  174.         {
  175.             self->monsterinfo.search (self);
  176.             self->monsterinfo.idle_time = level.time + 15 + random() * 15;
  177.         }
  178.         else
  179.         {
  180.             self->monsterinfo.idle_time = level.time + random() * 15;
  181.         }
  182.     }
  183. }
  184.  
  185.  
  186. /*
  187. =============
  188. ai_charge
  189.  
  190. Turns towards target and advances
  191. Use this call with a distnace of 0 to replace ai_face
  192. ==============
  193. */
  194. void ai_charge (edict_t *self, float dist)
  195. {
  196.     vec3_t    v;
  197.  
  198.     VectorSubtract (self->enemy->s.origin, self->s.origin, v);
  199.     self->ideal_yaw = vectoyaw(v);
  200.     M_ChangeYaw (self);
  201.  
  202.     if (dist)
  203.         M_walkmove (self, self->s.angles[YAW], dist);
  204. }
  205.  
  206.  
  207. /*
  208. =============
  209. ai_turn
  210.  
  211. don't move, but turn towards ideal_yaw
  212. Distance is for slight position adjustments needed by the animations
  213. =============
  214. */
  215. void ai_turn (edict_t *self, float dist)
  216. {
  217.     if (dist)
  218.         M_walkmove (self, self->s.angles[YAW], dist);
  219.  
  220.     if (FindTarget (self))
  221.         return;
  222.     
  223.     M_ChangeYaw (self);
  224. }
  225.  
  226.  
  227. /*
  228.  
  229. .enemy
  230. Will be world if not currently angry at anyone.
  231.  
  232. .movetarget
  233. The next path spot to walk toward.  If .enemy, ignore .movetarget.
  234. When an enemy is killed, the monster will try to return to it's path.
  235.  
  236. .hunt_time
  237. Set to time + something when the player is in sight, but movement straight for
  238. him is blocked.  This causes the monster to use wall following code for
  239. movement direction instead of sighting on the player.
  240.  
  241. .ideal_yaw
  242. A yaw angle of the intended direction, which will be turned towards at up
  243. to 45 deg / state.  If the enemy is in view and hunt_time is not active,
  244. this will be the exact line towards the enemy.
  245.  
  246. .pausetime
  247. A monster will leave it's stand state and head towards it's .movetarget when
  248. time > .pausetime.
  249.  
  250. walkmove(angle, speed) primitive is all or nothing
  251. */
  252.  
  253. /*
  254. =============
  255. range
  256.  
  257. returns the range catagorization of an entity reletive to self
  258. 0    melee range, will become hostile even if back is turned
  259. 1    visibility and infront, or visibility and show hostile
  260. 2    infront and show hostile
  261. 3    only triggered by damage
  262. =============
  263. */
  264. int range (edict_t *self, edict_t *other)
  265. {
  266.     vec3_t    v;
  267.     float    len;
  268.  
  269.     VectorSubtract (self->s.origin, other->s.origin, v);
  270.     len = VectorLength (v);
  271.     if (len < MELEE_DISTANCE)
  272.         return RANGE_MELEE;
  273.     if (len < 500)
  274.         return RANGE_NEAR;
  275.     if (len < 1000)
  276.         return RANGE_MID;
  277.     return RANGE_FAR;
  278. }
  279.  
  280. /*
  281. =============
  282. visible
  283.  
  284. returns 1 if the entity is visible to self, even if not infront ()
  285. =============
  286. */
  287. qboolean visible (edict_t *self, edict_t *other)
  288. {
  289.     vec3_t    spot1;
  290.     vec3_t    spot2;
  291.     trace_t    trace;
  292.  
  293.     VectorCopy (self->s.origin, spot1);
  294.     spot1[2] += self->viewheight;
  295.     VectorCopy (other->s.origin, spot2);
  296.     spot2[2] += other->viewheight;
  297.     trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
  298.     
  299.     if (trace.fraction == 1.0)
  300.         return true;
  301.     return false;
  302. }
  303.  
  304.  
  305. /*
  306. =============
  307. infront
  308.  
  309. returns 1 if the entity is in front (in sight) of self
  310. =============
  311. */
  312. qboolean infront (edict_t *self, edict_t *other)
  313. {
  314.     vec3_t    vec;
  315.     float    dot;
  316.     vec3_t    forward;
  317.     
  318.     AngleVectors (self->s.angles, forward, NULL, NULL);
  319.     VectorSubtract (other->s.origin, self->s.origin, vec);
  320.     VectorNormalize (vec);
  321.     dot = DotProduct (vec, forward);
  322.     
  323.     if (dot > 0.3)
  324.         return true;
  325.     return false;
  326. }
  327.  
  328.  
  329. //============================================================================
  330.  
  331. void HuntTarget (edict_t *self)
  332. {
  333.     vec3_t    vec;
  334.  
  335.     self->goalentity = self->enemy;
  336.     if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  337.         self->monsterinfo.stand (self);
  338.     else
  339.         self->monsterinfo.run (self);
  340.     VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
  341.     self->ideal_yaw = vectoyaw(vec);
  342.     // wait a while before first attack
  343.     if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
  344.         AttackFinished (self, 1);
  345. }
  346.  
  347. void FoundTarget (edict_t *self)
  348. {
  349.     // let other monsters see this monster for a while
  350.     if (self->enemy->client)
  351.     {
  352.         level.sight_entity = self;
  353.         level.sight_entity_framenum = level.framenum;
  354.         level.sight_entity->light_level = 128;
  355.     }
  356.  
  357.     self->show_hostile = level.time + 1;        // wake up other monsters
  358.  
  359.     VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
  360.     self->monsterinfo.trail_time = level.time;
  361.  
  362.     if (!self->combattarget)
  363.     {
  364.         HuntTarget (self);
  365.         return;
  366.     }
  367.  
  368.     self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
  369.     if (!self->movetarget)
  370.     {
  371.         self->goalentity = self->movetarget = self->enemy;
  372.         HuntTarget (self);
  373.         gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
  374.         return;
  375.     }
  376.  
  377.     // clear out our combattarget, these are a one shot deal
  378.     self->combattarget = NULL;
  379.     self->monsterinfo.aiflags |= AI_COMBAT_POINT;
  380.  
  381.     // clear the targetname, that point is ours!
  382.     self->movetarget->targetname = NULL;
  383.     self->monsterinfo.pausetime = 0;
  384.  
  385.     // run for it
  386.     self->monsterinfo.run (self);
  387. }
  388.  
  389.  
  390. /*
  391. ===========
  392. FindTarget
  393.  
  394. Self is currently not attacking anything, so try to find a target
  395.  
  396. Returns TRUE if an enemy was sighted
  397.  
  398. When a player fires a missile, the point of impact becomes a fakeplayer so
  399. that monsters that see the impact will respond as if they had seen the
  400. player.
  401.  
  402. To avoid spending too much time, only a single client (or fakeclient) is
  403. checked each frame.  This means multi player games will have slightly
  404. slower noticing monsters.
  405. ============
  406. */
  407. qboolean FindTarget (edict_t *self)
  408. {
  409.     edict_t        *client;
  410.     qboolean    heardit;
  411.     int            r;
  412.  
  413.     if (self->monsterinfo.aiflags & AI_GOOD_GUY)
  414.     {
  415.         if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
  416.         {
  417.             if (strcmp(self->goalentity->classname, "target_actor") == 0)
  418.                 return false;
  419.         }
  420.  
  421.         //FIXME look for monsters?
  422.         return false;
  423.     }
  424.  
  425.     // if we're going to a combat point, just proceed
  426.     if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  427.         return false;
  428.  
  429. // if the first spawnflag bit is set, the monster will only wake up on
  430. // really seeing the player, not another monster getting angry or hearing
  431. // something
  432.  
  433. // revised behavior so they will wake up if they "see" a player make a noise
  434. // but not weapon impact/explosion noises
  435.  
  436.     heardit = false;
  437.     if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
  438.     {
  439.         client = level.sight_entity;
  440.         if (client->enemy == self->enemy)
  441.         {
  442.             return false;
  443.         }
  444.     }
  445.     else if (level.sound_entity_framenum >= (level.framenum - 1))
  446.     {
  447.         client = level.sound_entity;
  448.         heardit = true;
  449.     }
  450.     else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
  451.     {
  452.         client = level.sound2_entity;
  453.         heardit = true;
  454.     }
  455.     else
  456.     {
  457.         client = level.sight_client;
  458.         if (!client)
  459.             return false;    // no clients to get mad at
  460.     }
  461.  
  462.     // if the entity went away, forget it
  463.     if (!client->inuse)
  464.         return false;
  465.  
  466.     if (client == self->enemy)
  467.         return true;    // JDC false;
  468.  
  469.     if (client->client)
  470.     {
  471.         if (client->flags & FL_NOTARGET)
  472.             return false;
  473.     }
  474.     else if (client->svflags & SVF_MONSTER)
  475.     {
  476.         if (!client->enemy)
  477.             return false;
  478.         if (client->enemy->flags & FL_NOTARGET)
  479.             return false;
  480.     }
  481.     else if (heardit)
  482.     {
  483.         if (client->owner->flags & FL_NOTARGET)
  484.             return false;
  485.     }
  486.     else
  487.         return false;
  488.  
  489.     if (!heardit)
  490.     {
  491.         r = range (self, client);
  492.  
  493.         if (r == RANGE_FAR)
  494.             return false;
  495.  
  496. // this is where we would check invisibility
  497.  
  498.         // is client in an spot too dark to be seen?
  499.         if (client->light_level <= 5)
  500.             return false;
  501.  
  502.         if (!visible (self, client))
  503.         {
  504.             return false;
  505.         }
  506.  
  507.         if (r == RANGE_NEAR)
  508.         {
  509.             if (client->show_hostile < level.time && !infront (self, client))
  510.             {
  511.                 return false;
  512.             }
  513.         }
  514.         else if (r == RANGE_MID)
  515.         {
  516.             if (!infront (self, client))
  517.             {
  518.                 return false;
  519.             }
  520.         }
  521.  
  522.         self->enemy = client;
  523.  
  524.         if (strcmp(self->enemy->classname, "player_noise") != 0)
  525.         {
  526.             self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  527.  
  528.             if (!self->enemy->client)
  529.             {
  530.                 self->enemy = self->enemy->enemy;
  531.                 if (!self->enemy->client)
  532.                 {
  533.                     self->enemy = NULL;
  534.                     return false;
  535.                 }
  536.             }
  537.         }
  538.     }
  539.     else    // heardit
  540.     {
  541.         vec3_t    temp;
  542.  
  543.         if (self->spawnflags & 1)
  544.         {
  545.             if (!visible (self, client))
  546.                 return false;
  547.         }
  548.         else
  549.         {
  550.             if (!gi.inPHS(self->s.origin, client->s.origin))
  551.                 return false;
  552.         }
  553.  
  554.         VectorSubtract (client->s.origin, self->s.origin, temp);
  555.  
  556.         if (VectorLength(temp) > 1000)    // too far to hear
  557.         {
  558.             return false;
  559.         }
  560.  
  561.         // check area portals - if they are different and not connected then we can't hear it
  562.         if (client->areanum != self->areanum)
  563.             if (!gi.AreasConnected(self->areanum, client->areanum))
  564.                 return false;
  565.  
  566.         self->ideal_yaw = vectoyaw(temp);
  567.         M_ChangeYaw (self);
  568.  
  569.         // hunt the sound for a bit; hopefully find the real player
  570.         self->monsterinfo.aiflags |= AI_SOUND_TARGET;
  571.         self->enemy = client;
  572.     }
  573.  
  574. //
  575. // got one
  576. //
  577.     FoundTarget (self);
  578.  
  579.     if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
  580.         self->monsterinfo.sight (self, self->enemy);
  581.  
  582.     return true;
  583. }
  584.  
  585.  
  586. //=============================================================================
  587.  
  588. /*
  589. ============
  590. FacingIdeal
  591.  
  592. ============
  593. */
  594. qboolean FacingIdeal(edict_t *self)
  595. {
  596.     float    delta;
  597.  
  598.     delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
  599.     if (delta > 45 && delta < 315)
  600.         return false;
  601.     return true;
  602. }
  603.  
  604.  
  605. //=============================================================================
  606.  
  607. qboolean M_CheckAttack (edict_t *self)
  608. {
  609.     vec3_t    spot1, spot2;
  610.     float    chance;
  611.     trace_t    tr;
  612.  
  613.     if (self->enemy->health > 0)
  614.     {
  615.     // see if any entities are in the way of the shot
  616.         VectorCopy (self->s.origin, spot1);
  617.         spot1[2] += self->viewheight;
  618.         VectorCopy (self->enemy->s.origin, spot2);
  619.         spot2[2] += self->enemy->viewheight;
  620.  
  621.         tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
  622.  
  623.         // do we have a clear shot?
  624.         if (tr.ent != self->enemy)
  625.             return false;
  626.     }
  627.     
  628.     // melee attack
  629.     if (enemy_range == RANGE_MELEE)
  630.     {
  631.         // don't always melee in easy mode
  632.         if (skill->value == 0 && (rand()&3) )
  633.             return false;
  634.         if (self->monsterinfo.melee)
  635.             self->monsterinfo.attack_state = AS_MELEE;
  636.         else
  637.             self->monsterinfo.attack_state = AS_MISSILE;
  638.         return true;
  639.     }
  640.     
  641. // missile attack
  642.     if (!self->monsterinfo.attack)
  643.         return false;
  644.         
  645.     if (level.time < self->monsterinfo.attack_finished)
  646.         return false;
  647.         
  648.     if (enemy_range == RANGE_FAR)
  649.         return false;
  650.  
  651.     if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  652.     {
  653.         chance = 0.4;
  654.     }
  655.     else if (enemy_range == RANGE_MELEE)
  656.     {
  657.         chance = 0.2;
  658.     }
  659.     else if (enemy_range == RANGE_NEAR)
  660.     {
  661.         chance = 0.1;
  662.     }
  663.     else if (enemy_range == RANGE_MID)
  664.     {
  665.         chance = 0.02;
  666.     }
  667.     else
  668.     {
  669.         return false;
  670.     }
  671.  
  672.     if (skill->value == 0)
  673.         chance *= 0.5;
  674.     else if (skill->value >= 2)
  675.         chance *= 2;
  676.  
  677.     if (random () < chance)
  678.     {
  679.         self->monsterinfo.attack_state = AS_MISSILE;
  680.         self->monsterinfo.attack_finished = level.time + 2*random();
  681.         return true;
  682.     }
  683.  
  684.     if (self->flags & FL_FLY)
  685.     {
  686.         if (random() < 0.3)
  687.             self->monsterinfo.attack_state = AS_SLIDING;
  688.         else
  689.             self->monsterinfo.attack_state = AS_STRAIGHT;
  690.     }
  691.  
  692.     return false;
  693. }
  694.  
  695.  
  696. /*
  697. =============
  698. ai_run_melee
  699.  
  700. Turn and close until within an angle to launch a melee attack
  701. =============
  702. */
  703. void ai_run_melee(edict_t *self)
  704. {
  705.     self->ideal_yaw = enemy_yaw;
  706.     M_ChangeYaw (self);
  707.  
  708.     if (FacingIdeal(self))
  709.     {
  710.         self->monsterinfo.melee (self);
  711.         self->monsterinfo.attack_state = AS_STRAIGHT;
  712.     }
  713. }
  714.  
  715.  
  716. /*
  717. =============
  718. ai_run_missile
  719.  
  720. Turn in place until within an angle to launch a missile attack
  721. =============
  722. */
  723. void ai_run_missile(edict_t *self)
  724. {
  725.     self->ideal_yaw = enemy_yaw;
  726.     M_ChangeYaw (self);
  727.  
  728.     if (FacingIdeal(self))
  729.     {
  730.         self->monsterinfo.attack (self);
  731.         self->monsterinfo.attack_state = AS_STRAIGHT;
  732.     }
  733. };
  734.  
  735.  
  736. /*
  737. =============
  738. ai_run_slide
  739.  
  740. Strafe sideways, but stay at aproximately the same range
  741. =============
  742. */
  743. void ai_run_slide(edict_t *self, float distance)
  744. {
  745.     float    ofs;
  746.     
  747.     self->ideal_yaw = enemy_yaw;
  748.     M_ChangeYaw (self);
  749.  
  750.     if (self->monsterinfo.lefty)
  751.         ofs = 90;
  752.     else
  753.         ofs = -90;
  754.     
  755.     if (M_walkmove (self, self->ideal_yaw + ofs, distance))
  756.         return;
  757.         
  758.     self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
  759.     M_walkmove (self, self->ideal_yaw - ofs, distance);
  760. }
  761.  
  762.  
  763. /*
  764. =============
  765. ai_checkattack
  766.  
  767. Decides if we're going to attack or do something else
  768. used by ai_run and ai_stand
  769. =============
  770. */
  771. qboolean ai_checkattack (edict_t *self, float dist)
  772. {
  773.     vec3_t        temp;
  774.     qboolean    hesDeadJim;
  775.  
  776. // this causes monsters to run blindly to the combat point w/o firing
  777.     if (self->goalentity)
  778.     {
  779.         if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  780.             return false;
  781.  
  782.         if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  783.         {
  784.             if ((level.time - self->enemy->teleport_time) > 5.0)
  785.             {
  786.                 if (self->goalentity == self->enemy)
  787.                     if (self->movetarget)
  788.                         self->goalentity = self->movetarget;
  789.                     else
  790.                         self->goalentity = NULL;
  791.                 self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  792.                 if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  793.                     self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  794.             }
  795.             else
  796.             {
  797.                 self->show_hostile = level.time + 1;
  798.                 return false;
  799.             }
  800.         }
  801.     }
  802.  
  803.     enemy_vis = false;
  804.  
  805. // see if the enemy is dead
  806.     hesDeadJim = false;
  807.     if ((!self->enemy) || (!self->enemy->inuse))
  808.     {
  809.         hesDeadJim = true;
  810.     }
  811.     else if (self->monsterinfo.aiflags & AI_MEDIC)
  812.     {
  813.         if (self->enemy->health > 0)
  814.         {
  815.             hesDeadJim = true;
  816.             self->monsterinfo.aiflags &= ~AI_MEDIC;
  817.         }
  818.     }
  819.     else
  820.     {
  821.         if (self->monsterinfo.aiflags & AI_BRUTAL)
  822.         {
  823.             if (self->enemy->health <= -80)
  824.                 hesDeadJim = true;
  825.         }
  826.         else
  827.         {
  828.             if (self->enemy->health <= 0)
  829.                 hesDeadJim = true;
  830.         }
  831.     }
  832.  
  833.     if (hesDeadJim)
  834.     {
  835.         self->enemy = NULL;
  836.     // FIXME: look all around for other targets
  837.         if (self->oldenemy && self->oldenemy->health > 0)
  838.         {
  839.             self->enemy = self->oldenemy;
  840.             self->oldenemy = NULL;
  841.             HuntTarget (self);
  842.         }
  843.         else
  844.         {
  845.             if (self->movetarget)
  846.             {
  847.                 self->goalentity = self->movetarget;
  848.                 self->monsterinfo.walk (self);
  849.             }
  850.             else
  851.             {
  852.                 // we need the pausetime otherwise the stand code
  853.                 // will just revert to walking with no target and
  854.                 // the monsters will wonder around aimlessly trying
  855.                 // to hunt the world entity
  856.                 self->monsterinfo.pausetime = level.time + 100000000;
  857.                 self->monsterinfo.stand (self);
  858.             }
  859.             return true;
  860.         }
  861.     }
  862.  
  863.     self->show_hostile = level.time + 1;        // wake up other monsters
  864.  
  865. // check knowledge of enemy
  866.     enemy_vis = visible(self, self->enemy);
  867.     if (enemy_vis)
  868.     {
  869.         self->monsterinfo.search_time = level.time + 5;
  870.         VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
  871.     }
  872.  
  873. // look for other coop players here
  874. //    if (coop && self->monsterinfo.search_time < level.time)
  875. //    {
  876. //        if (FindTarget (self))
  877. //            return true;
  878. //    }
  879.  
  880.     enemy_infront = infront(self, self->enemy);
  881.     enemy_range = range(self, self->enemy);
  882.     VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
  883.     enemy_yaw = vectoyaw(temp);
  884.  
  885.  
  886.     // JDC self->ideal_yaw = enemy_yaw;
  887.  
  888.     if (self->monsterinfo.attack_state == AS_MISSILE)
  889.     {
  890.         ai_run_missile (self);
  891.         return true;
  892.     }
  893.     if (self->monsterinfo.attack_state == AS_MELEE)
  894.     {
  895.         ai_run_melee (self);
  896.         return true;
  897.     }
  898.  
  899.     // if enemy is not currently visible, we will never attack
  900.     if (!enemy_vis)
  901.         return false;
  902.  
  903.     return self->monsterinfo.checkattack (self);
  904. }
  905.  
  906.  
  907. /*
  908. =============
  909. ai_run
  910.  
  911. The monster has an enemy it is trying to kill
  912. =============
  913. */
  914. void ai_run (edict_t *self, float dist)
  915. {
  916.     vec3_t        v;
  917.     edict_t        *tempgoal;
  918.     edict_t        *save;
  919.     qboolean    new;
  920.     edict_t        *marker;
  921.     float        d1, d2;
  922.     trace_t        tr;
  923.     vec3_t        v_forward, v_right;
  924.     float        left, center, right;
  925.     vec3_t        left_target, right_target;
  926.  
  927.     // if we're going to a combat point, just proceed
  928.     if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  929.     {
  930.         M_MoveToGoal (self, dist);
  931.         return;
  932.     }
  933.  
  934.     if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  935.     {
  936.         VectorSubtract (self->s.origin, self->enemy->s.origin, v);
  937.         if (VectorLength(v) < 64)
  938.         {
  939.             self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  940.             self->monsterinfo.stand (self);
  941.             return;
  942.         }
  943.  
  944.         M_MoveToGoal (self, dist);
  945.  
  946.         if (!FindTarget (self))
  947.             return;
  948.     }
  949.  
  950.     if (ai_checkattack (self, dist))
  951.         return;
  952.  
  953.     if (self->monsterinfo.attack_state == AS_SLIDING)
  954.     {
  955.         ai_run_slide (self, dist);
  956.         return;
  957.     }
  958.  
  959.     if (enemy_vis)
  960.     {
  961. //        if (self.aiflags & AI_LOST_SIGHT)
  962. //            dprint("regained sight\n");
  963.         M_MoveToGoal (self, dist);
  964.         self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  965.         VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
  966.         self->monsterinfo.trail_time = level.time;
  967.         return;
  968.     }
  969.  
  970.     // coop will change to another enemy if visible
  971.     if (coop->value)
  972.     {    // FIXME: insane guys get mad with this, which causes crashes!
  973.         if (FindTarget (self))
  974.             return;
  975.     }
  976.  
  977.     if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20)))
  978.     {
  979.         M_MoveToGoal (self, dist);
  980.         self->monsterinfo.search_time = 0;
  981. //        dprint("search timeout\n");
  982.         return;
  983.     }
  984.  
  985.     save = self->goalentity;
  986.     tempgoal = G_Spawn();
  987.     self->goalentity = tempgoal;
  988.  
  989.     new = false;
  990.  
  991.     if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
  992.     {
  993.         // just lost sight of the player, decide where to go first
  994. //        dprint("lost sight of player, last seen at "); dprint(vtos(self.last_sighting)); dprint("\n");
  995.         self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
  996.         self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
  997.         new = true;
  998.     }
  999.  
  1000.     if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
  1001.     {
  1002.         self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
  1003. //        dprint("reached current goal: "); dprint(vtos(self.origin)); dprint(" "); dprint(vtos(self.last_sighting)); dprint(" "); dprint(ftos(vlen(self.origin - self.last_sighting))); dprint("\n");
  1004.  
  1005.         // give ourself more time since we got this far
  1006.         self->monsterinfo.search_time = level.time + 5;
  1007.  
  1008.         if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
  1009.         {
  1010. //            dprint("was temp goal; retrying original\n");
  1011.             self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
  1012.             marker = NULL;
  1013.             VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
  1014.             new = true;
  1015.         }
  1016.         else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
  1017.         {
  1018.             self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
  1019.             marker = PlayerTrail_PickFirst (self);
  1020.         }
  1021.         else
  1022.         {
  1023.             marker = PlayerTrail_PickNext (self);
  1024.         }
  1025.  
  1026.         if (marker)
  1027.         {
  1028.             VectorCopy (marker->s.origin, self->monsterinfo.last_sighting);
  1029.             self->monsterinfo.trail_time = marker->timestamp;
  1030.             self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
  1031. //            dprint("heading is "); dprint(ftos(self.ideal_yaw)); dprint("\n");
  1032.  
  1033. //            debug_drawline(self.origin, self.last_sighting, 52);
  1034.             new = true;
  1035.         }
  1036.     }
  1037.  
  1038.     VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v);
  1039.     d1 = VectorLength(v);
  1040.     if (d1 <= dist)
  1041.     {
  1042.         self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
  1043.         dist = d1;
  1044.     }
  1045.  
  1046.     VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin);
  1047.  
  1048.     if (new)
  1049.     {
  1050. //        gi.dprintf("checking for course correction\n");
  1051.  
  1052.         tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
  1053.         if (tr.fraction < 1)
  1054.         {
  1055.             VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  1056.             d1 = VectorLength(v);
  1057.             center = tr.fraction;
  1058.             d2 = d1 * ((center+1)/2);
  1059.             self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1060.             AngleVectors(self->s.angles, v_forward, v_right, NULL);
  1061.  
  1062.             VectorSet(v, d2, -16, 0);
  1063.             G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
  1064.             tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
  1065.             left = tr.fraction;
  1066.  
  1067.             VectorSet(v, d2, 16, 0);
  1068.             G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
  1069.             tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
  1070.             right = tr.fraction;
  1071.  
  1072.             center = (d1*center)/d2;
  1073.             if (left >= center && left > right)
  1074.             {
  1075.                 if (left < 1)
  1076.                 {
  1077.                     VectorSet(v, d2 * left * 0.5, -16, 0);
  1078.                     G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
  1079. //                    gi.dprintf("incomplete path, go part way and adjust again\n");
  1080.                 }
  1081.                 VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
  1082.                 self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  1083.                 VectorCopy (left_target, self->goalentity->s.origin);
  1084.                 VectorCopy (left_target, self->monsterinfo.last_sighting);
  1085.                 VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  1086.                 self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1087. //                gi.dprintf("adjusted left\n");
  1088. //                debug_drawline(self.origin, self.last_sighting, 152);
  1089.             }
  1090.             else if (right >= center && right > left)
  1091.             {
  1092.                 if (right < 1)
  1093.                 {
  1094.                     VectorSet(v, d2 * right * 0.5, 16, 0);
  1095.                     G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
  1096. //                    gi.dprintf("incomplete path, go part way and adjust again\n");
  1097.                 }
  1098.                 VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
  1099.                 self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  1100.                 VectorCopy (right_target, self->goalentity->s.origin);
  1101.                 VectorCopy (right_target, self->monsterinfo.last_sighting);
  1102.                 VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  1103.                 self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1104. //                gi.dprintf("adjusted right\n");
  1105. //                debug_drawline(self.origin, self.last_sighting, 152);
  1106.             }
  1107.         }
  1108. //        else gi.dprintf("course was fine\n");
  1109.     }
  1110.  
  1111.     M_MoveToGoal (self, dist);
  1112.  
  1113.     G_FreeEdict(tempgoal);
  1114.  
  1115.     if (self)
  1116.         self->goalentity = save;
  1117. }
  1118.